﻿using System.Collections.Generic;
using System.Web.Mvc;
using System.Web.Security;
using DotNetOpenId;
using DotNetOpenId.RelyingParty;

namespace MvcMembership.Controllers
{
	public abstract class OpenIDController_Base : Controller
	{
		protected OpenIDController_Base()
			: this( null, null ) {}

		protected OpenIDController_Base( IFormsAuthentication formsAuth, MembershipProvider provider )
		{
			FormsAuth = formsAuth ?? new FormsAuthenticationWrapper();
			Provider = provider ?? Membership.Provider;
		}

		public IFormsAuthentication FormsAuth { get; private set; }
		public MembershipProvider Provider { get; private set; }

		[HandleError( ExceptionType = typeof(OpenIdException) )]
		[HandleError( ExceptionType = typeof(System.Net.WebException) )]
		public virtual ActionResult Login( string openid_identifier )
		{
			//### set page title & declare variables
			ViewData["Title"] = "Login via OpenID";
			var errors = new List<string>();
			var rememberMe = false;
			var openid = new OpenIdRelyingParty();

			//### stage 1: display login form to user
			if( openid.Response == null && Request.HttpMethod != "POST" )
				return View();

			if( openid.Response == null )
			{
				//### stage 2: user submitting Identifier
				Identifier id;
				if( Identifier.TryParse( openid_identifier, out id ) )
					openid.CreateRequest( openid_identifier ).RedirectToProvider();
				else
				{
					errors.Add( ErrorMessages.InvalidIdentifierSpecified );
					ViewData["errors"] = errors;
					ViewData["openid_identifier"] = openid_identifier;
					return View();
				}
			}
			else
			{
				//### stage 3: OpenID Provider sending assertion response
				switch(openid.Response.Status)
				{
					case AuthenticationStatus.Authenticated:

						//### associate openid identity to user account and login
						var userName = AssociateOpenIdIdentityToUserName( openid.Response.ClaimedIdentifier );
						if( string.IsNullOrEmpty( userName ) )
						{
							errors.Add( ErrorMessages.AssociationFailure );
							ViewData["errors"] = errors;
							ViewData["openid_identifier"] = openid.Response.ClaimedIdentifier;
							return View();
						}
						FormsAuthentication.RedirectFromLoginPage( userName, rememberMe );
						break;

					case AuthenticationStatus.Canceled:
						errors.Add( ErrorMessages.CanceledAtProvider );
						ViewData["errors"] = errors;
						ViewData["openid_identifier"] = openid.Response.ClaimedIdentifier;
						return View();
					case AuthenticationStatus.Failed:
						errors.Add( ErrorMessages.UnknownFailure + openid.Response.Exception.Message );
						ViewData["errors"] = errors;
						ViewData["openid_identifier"] = openid.Response.ClaimedIdentifier;
						return View();
				}
			}
			return new EmptyResult();
		}

		public virtual ViewResult XRDS()
		{
			return View();
		}

		[NonAction]
		protected abstract string AssociateOpenIdIdentityToUserName( string openIdIdentity );

		#region Nested type: ErrorMessages

		public struct ErrorMessages
		{
			public static string AssociationFailure = "Could not associate OpenID to a user in this system.";
			public static string CanceledAtProvider = "Login canceled by OpenID provider.";
			public static string InvalidIdentifierSpecified = "You enter your OpenID Url.";
			public static string InvalidIdentityServerSpecified = "OpenID provider not on whitelist.";
			public static string UnknownFailure = "OpenID authentication failed: ";
		}

		#endregion
	}
}